package cn.zwx.mvcframework.aop.support;

import cn.zwx.mvcframework.aop.advice.ZwxAdvice;
import cn.zwx.mvcframework.aop.config.ZwxAopConfig;
import lombok.Data;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 切入点工具类
 * @author zhangwenxue
 */
@Data
public class ZwxAdvisedSupport {

    /**
     * Aop配置信息
     */
    private ZwxAopConfig config;
    /**
     * 目标类
     */
    private Object target;
    /**
     * 目标类Class对象
     */
    private Class targetClass;
    /**
     * 切入表达式
     */
    private Pattern pointCutClassPattern;

    /**
     * 搞一个切入点缓存
     */
    private Map<Method, Map<String, ZwxAdvice>> methodCache;

    public ZwxAdvisedSupport(ZwxAopConfig config) {
        this.config = config;
    }

    public void setTargetClass(Class targetClass) {
        this.targetClass = targetClass;
        // 解析目标类 把目前符合切入表达式转为为一个个切入点
        this.parse();
    }

    private void parse() {
        //把Spring的Excpress变成Java能够识别的正则表达式
        String pointCut = "public .* cn\\.zwx\\.demo\\.service\\..*Service.*\\(.*\\)";
//                .replaceAll("\\.", "\\\\.")
//                .replaceAll("\\\\.\\*", ".*")
//                .replaceAll("\\(", "\\\\(")
//                .replaceAll("\\)", "\\\\)");

        // 转换为专门匹配Class的正则  public .* cn.zwx.demo.service..*Service..*(.*) ==>  public .* cn.zwx.demo.service..*Service
        String pointCutForClassRegex = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
        // public .* cn.zwx.demo.service..*Service ==> class  public .* cn.zwx.demo.service..*Service
        pointCutClassPattern = Pattern.compile("class " + pointCutForClassRegex.substring(pointCutForClassRegex.lastIndexOf(" ") + 1));
        // 初始化缓存
        methodCache= new HashMap<>(8);
        // 编译正则
        Pattern compile = Pattern.compile(pointCut);
        // 获取切面类
        String aspectClassStr = config.getAspectClass();
        try {
            Class<?> aspectClass = Class.forName(aspectClassStr);
            // 保存切面类方法和方法对象的集合
            Map<String, Method> aspectClassMethod = new HashMap<>(8);
            for (Method method : aspectClass.getMethods()) {
                aspectClassMethod.put(method.getName(),method);
            }
            // 根据目标类方法 生成执行器链路
            for (Method method : this.targetClass.getMethods()) {
                String methodToString = method.toString();
                if (methodToString.contains("throws")){
                    // 截取掉throws部分
                    methodToString = methodToString.substring(0,methodToString.lastIndexOf("throws")).trim();
                }
                // 跟之前编译好的正则进行匹配
                Matcher matcher = compile.matcher(methodToString);

                if (matcher.find()){
                    // 执行器链
                    Map<String, ZwxAdvice> adviceHashMap = new HashMap<>(8);
                    // 把每个方法包装成一个MethodInterceptor
                    if (!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))){
                        adviceHashMap.put(config.getAspectBefore(),new ZwxAdvice(aspectClass.newInstance(),aspectClassMethod.get(config.getAspectBefore())));
                    }
                    if (!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))){
                        adviceHashMap.put(config.getAspectAfter(),new ZwxAdvice(aspectClass.newInstance(),aspectClassMethod.get(config.getAspectAfter())));
                    }
                    if (!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))){
                        adviceHashMap.put(config.getAspectAfterThrow(),new ZwxAdvice(aspectClass.newInstance(),aspectClassMethod.get(config.getAspectAfterThrow())
                                ,config.getAspectAfterThrowingName()));
                    }
                    methodCache.put(method,adviceHashMap);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }


    /**
     * 根据一个目标代理类的方法，获得其对应的通知
     * @param targetMethod 目标代理对象方法
     * @throws Exception e 业务异常
     */
    public Map<String,ZwxAdvice> getAdvices(Method targetMethod) throws Exception {
        Map<String, ZwxAdvice> stringZwxAdviceMap = methodCache.get(targetMethod);
        if (null == stringZwxAdviceMap){
            // 根据方法签名再去拿一次
            Method method = targetClass.getMethod(targetMethod.getName(), targetMethod.getParameterTypes());
            stringZwxAdviceMap = methodCache.get(method);
            this.methodCache.put(method,stringZwxAdviceMap);
        }
        return stringZwxAdviceMap;
    }

    /**
     * 给ApplicationContext首先IoC中的对象初始化时调用，决定要不要生成代理类的逻辑
     * @return boolean
     */
    public boolean pointCutMath() {
        return pointCutClassPattern.matcher(this.targetClass.toString()).find();
    }

}